package extract.knowledge.general;

import com.thinkaurelius.titan.core.*;
import com.thinkaurelius.titan.core.schema.RelationTypeIndex;
import com.thinkaurelius.titan.core.schema.TitanGraphIndex;
import com.thinkaurelius.titan.core.schema.TitanManagement;
import com.thinkaurelius.titan.graphdb.database.management.ManagementSystem;
import common.model.graph.CommonSchema;
import common.model.graph.MetaSchema;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import common.dao.gremlin.TitanGraphDao;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Common on 9/9/16.
 */
public class BaseManagement {

    private  final Logger logger = LoggerFactory.getLogger(BaseManagement.class);

    public String com_index_prefix = "composite_";
    public String mix_index_prefix = "mixeindex_";
    public String edg_index_prefix = "edgeindex_";
    public String index_backend_name = "search";  // index.search.backend

    public enum TitanIndexType{
        Composite, Mixed
    }

    public   String id_key;
    public   String label_value;
    public   String node_type_key;
    public   String node_type_value;
    public   String has_name_key;
    public   String has_name_zh_key;
    public   String has_name_en_key;

    public final TitanGraph graph;
    public final GraphTraversalSource g;

    @Inject
    public BaseManagement(TitanGraph graph){
        this.graph = graph;
        this.g = graph.traversal();

        this.id_key = CommonSchema.Properties.has_id;
        this.node_type_key = CommonSchema.Properties.has_node_type;

        this.has_name_key = CommonSchema.Properties.has_name;
        this.has_name_zh_key = CommonSchema.Properties.has_name_zh;
        this.has_name_en_key = CommonSchema.Properties.has_name_en;
    }




    ///////////////////////////////////////////////////

    public boolean createProperty(String propertyName, Class dataType){
        //graph.tx().rollback(); //Never create new indexes while a transaction is active
        logger.info("[createProperty] " + propertyName);
        //TitanTransaction tx = graph.newTransaction();
        TitanManagement mgmt = null;
        PropertyKey property = null;
        // 1) create property
        mgmt = graph.openManagement();
        property = mgmt.makePropertyKey(propertyName).dataType( dataType ).make();
        mgmt.commit();
        //tx.commit();
        return true;
    }

    public boolean createVertexLabel(String vertexLabel){
        //graph.tx().rollback(); //Never create new indexes while a transaction is active
        logger.info("[createVertexLabel] " +  vertexLabel);
        //TitanTransaction tx = graph.newTransaction();
        TitanManagement mgmt = null;
        mgmt = graph.openManagement();
        mgmt.makeVertexLabel(vertexLabel ).make();
        mgmt.commit();
        //tx.commit();
        return true;
    }

    public boolean createEdgeLabel(String edgeLabel, Multiplicity multi){
        //graph.tx().rollback(); //Never create new indexes while a transaction is active
        logger.info("[createEdgeLabel] " + edgeLabel );
        //TitanTransaction tx = graph.newTransaction();
        TitanManagement mgmt = null;
        mgmt = graph.openManagement();
        mgmt.makeEdgeLabel(edgeLabel).multiplicity(multi).make();
        mgmt.commit();
        //tx.commit();
        return true;
    }


    public boolean createPropertyIndex(String propertyName,Class dataType){
        TitanManagement mgmt = graph.openManagement();
        final PropertyKey property = mgmt.makePropertyKey( propertyName ).dataType( dataType ).make();
        String indexName = this.com_index_prefix + propertyName;
        TitanManagement.IndexBuilder nameIndexBuilder = mgmt.buildIndex(indexName, Vertex.class).addKey(property);
        TitanGraphIndex propertyIndex = nameIndexBuilder.buildCompositeIndex();
        mgmt.commit();
        logger.info("[createPropertyIndex] " + propertyName + "committed!");
        return true;
    }


    public boolean createGraphIndexes(List<String> propertyNames, boolean isUnique, TitanIndexType indexType,boolean reindex){
        /*
        1) 创建的索引不成功，不仅速度没有增快，还在gremlin console中报警，提示没有使用索引
        2）后来数据也存不进去了

         */
        //graph.tx().rollback(); //Never create new indexes while a transaction is active
        //TitanTransaction tx = graph.newTransaction();

        TitanManagement mgmt = null;
        ManagementSystem mgmt2 = null;
        PropertyKey property = null;
        String indexName = "";
        if(indexType == TitanIndexType.Composite)
            indexName += (com_index_prefix);
        else
            indexName += (mix_index_prefix);
        for(String p : propertyNames)
            indexName += ("_" + p);
        logger.info("[createGraphIndexes] " +  indexName);

        //2) get property and build index
        mgmt = graph.openManagement();
        TitanManagement.IndexBuilder builder = mgmt.buildIndex(indexName, Vertex.class);
        for(String p : propertyNames){
            property = mgmt.getPropertyKey(p);
            builder.addKey( property );
            //addKey(property,Mapping.TEXT.getParameter())
        }
        if(isUnique && indexType==TitanIndexType.Composite)
            builder.unique();
        TitanGraphIndex index = null;
        if(indexType == TitanIndexType.Mixed)
            index = builder.buildMixedIndex(this.index_backend_name);
        else
            index = builder.buildCompositeIndex();
        //index = mgmt.getGraphIndex(indexName);
        mgmt.commit();
        logger.info("[createGraphIndexes] 1) created -> waiting");

        // 3) Wait for the index to become available
        // https://github.com/thinkaurelius/titan/issues/1324
        /*
        mgmt2 = (ManagementSystem) graph.openManagement();
        try {
            mgmt2.awaitGraphIndexStatus(graph, indexName).call();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mgmt2.commit();
        */
        logger.info("[createGraphIndexes] 2) waiting finished");

        // 4) Reindex the existing data
        /*
        mgmt = graph.openManagement();
        try {
            if(reindex)
                mgmt.updateIndex(mgmt.getGraphIndex( indexName ), SchemaAction.REINDEX).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        mgmt.commit();
        */
        logger.info("[createGraphIndexes] 3) reindex");

        //tx.commit();
        logger.info("[createGraphIndexes] " +  indexName + " finished!");
        return true;
    }

    public boolean createEdgeIndexes(String edgeLabel, List<String> propertyNames, Direction direction, Order order, boolean reindex){
        // Vertex-centric indexes are local index structures built individually per vertex.
        // Direction.BOTH
        // Order.decr
        //graph.tx().rollback();
        //TitanTransaction tx = graph.newTransaction();
        TitanManagement mgmt = null;
        ManagementSystem mgmt2 = null;
        String indexName = edg_index_prefix;
        indexName += ("_" + edgeLabel);
        for(String p : propertyNames)
            indexName += ("_" + p);
        logger.info("[createEdgeIndexes] " + indexName);

        mgmt = graph.openManagement();
        List<PropertyKey> pKeys = new ArrayList<>();
        for(String p: propertyNames){
            PropertyKey property = mgmt.getPropertyKey(p);
            pKeys.add(property);
        }
        PropertyKey[] pKeysArray = (PropertyKey[])pKeys.toArray();
        EdgeLabel edge = mgmt.getEdgeLabel(edgeLabel);
        RelationTypeIndex index = mgmt.buildEdgeIndex(edge, indexName, direction, order, pKeysArray);
        //mgmt.buildPropertyIndex()
        mgmt.commit();
        //Wait for the index to become available
        /*
        mgmt2 = (ManagementSystem) graph.openManagement();
        try {
            mgmt2.awaitGraphIndexStatus(graph, indexName).call();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mgmt2.commit();
        */
        //Reindex the existing data
        /*
        mgmt = graph.openManagement();
        try {
            if(reindex)
                mgmt.updateIndex(mgmt.getGraphIndex(indexName), SchemaAction.REINDEX).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        mgmt.commit();
        */
        logger.info("[createEdgeIndexes] " +  indexName + " finished!");
        //tx.commit();
        return true;
    }


    /////////////////////////////////////////////////////////

    public boolean createBaseProperties(){

        createProperty(this.has_name_key, String.class);
        createProperty(this.has_name_zh_key, String.class);
        createProperty(this.has_name_en_key, String.class);

        createProperty(this.node_type_key, String.class);
        createProperty(this.id_key, Integer.class);

        return true;
    }

    public boolean createBasePropertyIndexes(){
        createPropertyIndex(this.has_name_key, String.class);
        createPropertyIndex(this.has_name_zh_key, String.class);
        createPropertyIndex(this.has_name_en_key, String.class);

        createPropertyIndex(this.node_type_key, String.class);
        createPropertyIndex(this.id_key, Integer.class);

        return true;
    }


    public boolean createBaseGraphIndexes(){
        // 1) name property

        List<String> has_name_key_list = new ArrayList<>();
        has_name_key_list.add(this.has_name_key);
        createGraphIndexes(has_name_key_list, false, TitanIndexType.Composite, false);
        pauseAWhile();
        //createGraphIndexes(has_name_key_list, false, TitanIndexType.Mixed ,false);
        //pauseAWhile();

        //
        List<String> has_name_zh_key_list = new ArrayList<>();
        has_name_zh_key_list.add(this.has_name_zh_key);
        createGraphIndexes(has_name_zh_key_list, false, TitanIndexType.Composite, false);
        pauseAWhile();
        //createGraphIndexes(has_name_zh_key_list, false, TitanIndexType.Mixed,false);
        //pauseAWhile();
        //

        List<String> has_name_en_key_list = new ArrayList<>();
        has_name_en_key_list.add(this.has_name_en_key);
        createGraphIndexes(has_name_en_key_list, false, TitanIndexType.Composite,false);
        pauseAWhile();
        //createGraphIndexes(has_name_en_key_list, false, TitanIndexType.Mixed,false);
        //pauseAWhile();

        // 2) label (type)
        List<String> node_type_key_list = new ArrayList<>();
        node_type_key_list.add(this.node_type_key);
        createGraphIndexes(node_type_key_list, false, TitanIndexType.Composite,false);
        pauseAWhile();

        // 3) id (自定义）
        List<String> id_key_list = new ArrayList<>();
        id_key_list.add(this.id_key);
        createGraphIndexes(id_key_list, false, TitanIndexType.Composite,false);
        pauseAWhile();

        return true;
    }

    public boolean createBaseVertexLabels(){
        createVertexLabel(MetaSchema.concept);
        createVertexLabel(MetaSchema.instance);
        createVertexLabel(MetaSchema.edge_class);
        logger.info("[createBaseVertexLabels] success!");
        return true;
    }

    public boolean createBaseEdgeLabels(){

        return true;
    }



    public boolean createBaseEdgeIndexes(){

        return true;
    }

    public void pauseAWhile(){
        int SLEEP_TIME = 2000;
        logger.info("[Sleep a while]");
        try {
            Thread.sleep(SLEEP_TIME);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public List<String> getAllIndexes(){
        TitanManagement mgmt = null;
        mgmt = graph.openManagement();
        //mgmt.getGraphIndexes()
        return null;
    }

    /*******************************
     *
     * @return
     */
    public boolean runBaseManagement(){

        createBaseVertexLabels();

        createBasePropertyIndexes();

        //createBaseProperties();



        //createBaseGraphIndexes();

        logger.info("[runBaseManagement] successed!" );
        return true;
    }


    /*****************************************
     *
     * @param args
     */
    public static void main(String[] args){

        // cleanup
        new TitanGraphDao().cleanUpGraph();

        BaseManagement manage = new BaseManagement( new TitanGraphDao("debug").getTitanGraph() );
        //manage.createBaseProperties();
        //manage.createBaseGraphIndexes();
        /*
        manage.createProperty( CommonSchema.Properties.has_id );
        List<String> has_name_key_list = new ArrayList<>();
        has_name_key_list.add( CommonSchema.Properties.has_id );
        manage.createGraphIndexes( has_name_key_list,false, TitanIndexType.Composite,false );
        */
        manage.runBaseManagement();


        /*
        1) titan.sh clean
        Exception:
            com.thinkaurelius.titan.diskstorage.ResourceUnavailableException: Log [systemlog] has been closed
            at com.thinkaurelius.titan.diskstorage.ResourceUnavailableException.verifyOpen(ResourceUnavailableException.java:56)
	        at com.thinkaurelius.titan.diskstorage.log.kcvs.KCVSLog.add(KCVSLog.java:398)
        Ref:
            http://titan.thinkaurelius.com/javadoc/current/com/thinkaurelius/titan/diskstorage/ResourceUnavailableException.html

         */

        /*
        2)
        com.thinkaurelius.titan.core.TitanException: Could not re-open management log
         */

        /*
        3)
         */

    }
}
